package com.atguigu.tingshu.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.config.ThreadPoolExecutorConfiguration;
import com.atguigu.tingshu.search.repository.AlbumIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.album.AlbumStatVoMsg;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexSearchVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.*;

/**
 * 方案一：利用线程池threadPoolExecutor创建的多线程异步远程调用，可以提高程序执行效率（最大运行时长为获取专辑基本数据时间+多线程异步调用最长耗时），并且通过线程工具类CountDownLatch(3)来确保耗时最长的异步线程也执行完成任务。
 */

@Slf4j
//@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class SearchServiceImpl0 implements SearchService {
    //es调用增删修改Es连接库中的数据
    @Autowired
    AlbumIndexRepository albumIndexRepository;
    //远程服务调用
    @Autowired
    AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    CategoryFeignClient categoryFeignClient;

    @Autowired
    UserInfoFeignClient userInfoFeignClient;

    @Autowired
    ThreadPoolExecutorConfiguration threadPoolExecutorConfiguration;
    @Autowired
    ThreadPoolExecutor threadPoolExecutor;

    @Override
    @SneakyThrows
    public void upperAlbum(Long albumId) {
        //线程并发工具类
        CountDownLatch countDownLatch = new CountDownLatch(3);
        //构建AlbumInfoIndex对象
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();

        //专辑基本数据，请求专辑微服务根据专辑的id查询专辑基本数据
        Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfoById(albumId);
        AlbumInfo albumInfo = albumInfoResult.getData();
        BeanUtils.copyProperties(albumInfo, albumInfoIndex);
        albumInfoIndex.setIsFinished(String.valueOf(albumInfo.getIsFinished()));

        //请求专辑微服务根据专辑id获取专辑属性值列表
        List<AlbumAttributeValue> attributeValueList = albumInfo.getAlbumAttributeValueVoList();
        //将AlbumAttributeValue中的属性ID和属性值ID复制到attributeValueIndex中
        List<AttributeValueIndex> attributeValueIndexList = attributeValueList.stream().map(attributeValue -> {
            AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
            BeanUtils.copyProperties(attributeValue, attributeValueIndex);
            return attributeValueIndex;
        }).toList();
        albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);

        //请求专辑微服务根据专辑三级分类的id查询专辑分类信息
        threadPoolExecutor.execute(()->{
            Result<BaseCategoryView> baseCategoryViewResult = categoryFeignClient.findCategoryByC3Id(albumInfo.getCategory3Id());
            BaseCategoryView baseCategoryView = baseCategoryViewResult.getData();
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
            albumInfoIndex.setCategory3Id(baseCategoryView.getCategory3Id());
            countDownLatch.countDown();
        });

        //请求用户微服务根据专辑作者id获取用户数据
        threadPoolExecutor.execute(()->{
            Result<UserInfo> userInfoResult = userInfoFeignClient.findUserInfoById(albumInfo.getUserId());
            UserInfo userInfo = userInfoResult.getData();
            albumInfoIndex.setAnnouncerName(userInfo.getNickname());
            countDownLatch.countDown();
        });

        //请求专辑微服务根据专辑id获取专辑的统计数据
        threadPoolExecutor.execute(()->{
            Result<AlbumStatVo> albumStatVoResult = albumInfoFeignClient.findAlbumStatByAlbumId(albumId);
            AlbumStatVo albumStatVo = albumStatVoResult.getData();
            Integer playStatNum = albumStatVo.getPlayStatNum();
            albumInfoIndex.setPlayStatNum(playStatNum);
            Integer subscribeStatNum = albumStatVo.getSubscribeStatNum();
            albumInfoIndex.setSubscribeStatNum(subscribeStatNum);
            Integer buyStatNum = albumStatVo.getBuyStatNum();
            albumInfoIndex.setBuyStatNum(buyStatNum);
            Integer commentStatNum = albumStatVo.getCommentStatNum();
            albumInfoIndex.setCommentStatNum(commentStatNum);
            albumInfoIndex.setHotScore(playStatNum*1.0+subscribeStatNum*2.0+buyStatNum*3.0+commentStatNum*4.0);
            countDownLatch.countDown();
        });

        countDownLatch.await();
        log.info(JSON.toJSONString(albumInfoIndex));
        //将AlbumInfoIndex对象保存到ES索引库中
        albumIndexRepository.save(albumInfoIndex);
    }

    @Override
    public void lowerAlbum(Long albumId) {
        albumIndexRepository.deleteById(albumId);
    }

    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {
        //备用
        return null;
    }

    @Override
    public List<AlbumInfoIndexSearchVo> channel(Long category1Id) {
        return null;
    }

    @Override
    public List<String> completeSuggest(String keyword) {
        return null;
    }

    @Override
    public void updateLatelyAlbumRanking() {

    }

    @Override
    public List<AlbumInfoIndexVo> findRankingList(Long category1Id, Long dimension) {
        return null;
    }

    @Override
    public void updateAlbumStat(AlbumStatVoMsg albumStatVoMsg) {

    }


    public static void main(String[] args) throws Exception {
        //demo01();
        //demo02();
        //demo03();
        //demo04();
        //demo05();
        //demo06();
        demo07();
        while(true);
    }

    private static void demo07() {
        CompletableFuture<Void> c1 = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + "任务1执行完毕");
        });
        CompletableFuture<Integer> c2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + "任务2执行完毕");
            return 451;
        });
        c1.runAfterBoth(c1,()->{
            System.out.println(Thread.currentThread().getName() + "任务3执行完毕");
        }).join();
        System.out.println(Thread.currentThread().getName() + "任务都执行完毕。。。");
    }

    private static void demo06() throws ExecutionException, InterruptedException {
        CompletableFuture<Void> c1 = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + "任务1执行完毕");
        });
        CompletableFuture<Integer> c2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + "任务2执行完毕");
            return 451;
        });

        //Void unused = CompletableFuture.allOf(c1, c2).get();
        //Void unused = CompletableFuture.allOf(c1, c2).join();
        Object o = CompletableFuture.anyOf(c1, c2).join();
        System.out.println(Thread.currentThread().getName() + "任务都执行完毕。。。"+"..."+o);
    }

    private static void demo05() throws Exception {
        Integer integer = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "1执行了");
            return 1000;
        }).thenApply(result -> {
            System.out.println(Thread.currentThread().getName() + "2执行了");
            return result + 450;
        }).get();
        System.out.println(integer);
    }

    private static void demo04() {
        CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+"1执行了");
            return 1024;
        }).thenAcceptAsync(result->{
            System.out.println(Thread.currentThread().getName()+"2执行了");
            System.out.println(2*result);
        });
    }

    private static void demo03() {
        CompletableFuture.runAsync(()->{
            System.out.println(Thread.currentThread().getName()+"执行了");
        }).thenRunAsync(()->{
            System.out.println(Thread.currentThread().getName()+"执行了");
        });
    }

    private static void demo02() throws Exception {
        Integer integer = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "执行了");
            return 1024;
        }).get();
        System.out.println(integer);
    }

    private static void demo01() {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        CompletableFuture.runAsync(()->{
            System.out.println(Thread.currentThread().getName()+"执行了");
        },executorService);
    }
}